package database

import (
	"context"
	"time"

	"github.com/star-find-cloud/star-chat/conf"
	log "github.com/star-find-cloud/star-chat/pkg/logger"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
	"sync"
)

type MongoDB struct {
	Client   *mongo.Client
	Database *mongo.Database
}

var (
	_mongoDB = &MongoDB{}
	_onceMongo sync.Once
)

func init() {
	_onceMongo.Do(func() {
		c := conf.GetConfig()
		
		// 设置MongoDB连接选项
		clientOptions := options.Client().
			ApplyURI(c.Database.MongoDB.URI).
			SetMaxPoolSize(c.Database.MongoDB.MaxPoolSize).
			SetMinPoolSize(c.Database.MongoDB.MinPoolSize).
			SetSocketTimeout(time.Duration(c.Database.MongoDB.SocketTimeout) * time.Second)

		// 连接到MongoDB
		client, err := mongo.Connect(context.Background(), clientOptions)
		if err != nil {
			log.MongoDBLogger.Errorf("MongoDB connect failed: %s\n", err)
			return
		}

		// 检查连接
		err = client.Ping(context.Background(), nil)
		if err != nil {
			log.MongoDBLogger.Errorf("MongoDB ping failed: %s\n", err)
			return
		}

		// 获取数据库实例
		db := client.Database(c.Database.MongoDB.Database)

		_mongoDB.Client = client
		_mongoDB.Database = db
		log.MongoDBLogger.Infof("MongoDB connection successful: %s\n", c.Database.MongoDB.URI)
	})
}

func GetMongoDB() NoSQLDatabase {
	return _mongoDB
}

func (m MongoDB) GetClient() *mongo.Client {
	return m.Client
}

func (m MongoDB) GetDatabase() *mongo.Database {
	return m.Database
}

func (m MongoDB) GetCollection(name string) *mongo.Collection {
	return m.Database.Collection(name)
}

func (m MongoDB) Ping() error {
	return m.Client.Ping(context.Background(), nil)
}

func (m MongoDB) Close() error {
	return m.Client.Disconnect(context.Background())
}

// WithTransaction 执行MongoDB事务
func (m MongoDB) WithTransaction(ctx context.Context, fn func(session mongo.Session) error) error {
	session, err := m.Client.StartSession()
	if err != nil {
		return err
	}
	defer session.EndSession(ctx)

	return mongo.WithSession(ctx, session, func(sessionContext mongo.SessionContext) error {
		if err := session.StartTransaction(); err != nil {
			return err
		}

		if err := fn(session); err != nil {
			session.AbortTransaction(sessionContext)
			return err
		}

		return session.CommitTransaction(sessionContext)
	})
}